home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr05 / todo102.zip / FM_UTL11.ZIP / FM_UTILS.C next >
C/C++ Source or Header  |  1992-08-15  |  23KB  |  720 lines

  1. //***************************************************************************
  2. //
  3. //    Library:
  4. //      FM_UTILS.DLL - version 1.1a
  5. //
  6. //    Purpose:
  7. //        FM_UTILS is a File Manager extension DLL. An extension DLL adds
  8. //        a menu to File Manager, contains an entry point that processes menu
  9. //        commands and notification messages sent by File Manager, and
  10. //        queries data and information about the File Manager windows. The 
  11. //        purpose of an extension DLL is to add administration support 
  12. //        features to File Manager, for example, file and disk utilities.
  13. //        Up to five extension DLLs may be installed at any one time.
  14. //
  15. //        FM_UTILS adds a menu (called "Utilities" by default) to File Manager
  16. //        and processes all the messages that are sent by File Manager to an 
  17. //        extension DLL. In order to retrieve any information, it sends 
  18. //        messages to File Manager.
  19. //
  20. //    Usage:
  21. //        File Manager installs the extensions that have entries in the 
  22. //        [AddOns] section of the WINFILE.INI initialization file. An entry 
  23. //        consists of a tag and a value. To load FM_UTILS.DLL as a File 
  24. //        Manager extension, add the following to WINFILE.INI (assuming the 
  25. //        DLL resides in c:\win\system):
  26. //
  27. //            [AddOns]
  28. //            FM_Utils Extension=fm_utils.dll
  29. //
  30. //        In addition, a section specific to this Extension DLL is added to
  31. //        WINFILE.INI. There are several elements to this section:
  32. //        For each utility to appear on the menu, there is a 'UserProgn=' tag,
  33. //        and a 'UserDescn=' tag. Comments (preceded by semicolons) are optional.
  34. //        You may have up to twenty separate utilities specified.
  35. //        You can also specify up to five utilities to load at the same time as
  36. //        FileMan, and exit when you close FileMan. You use the 'PreLoadn='
  37. //        tag for this. All of the utilities are started with the SW_SHOWMINNOACTIVE
  38. //        setting (minimized, but not activated).
  39. //        And, you can specify the name of the added menu. If you choose not to,
  40. //        the default is "&Utilities". This provides foreign language support,
  41. //        as well as allowing you to use a very short name, to keep the menu bar
  42. //        from growing too long. The tag to use is 'UserMenuName='.
  43. //
  44. //            [FM_Utils Extension]
  45. //            ;Name the menu
  46. //            UserMenuName=&Util
  47. //            ;add Upper Deck Editor to menu
  48. //            UserDesc1=&Edit File(s)
  49. //            UserProg1=D:\UDE\UDE.EXE
  50. //            ;add V. Buerg's LIST to menu
  51. //            UserDesc2=&View File(s)
  52. //            UserProg2=LIST.PIF
  53. //            ;PreLoad Trash Can
  54. //            PreLoad1=TRASH.EXE
  55. //
  56. //    Revision History:
  57. //        1.0 - March '92 - initial release
  58. //
  59. //        1.1 - April '92 - added ability to PreLoad utilities. This seems even
  60. //                            more popular than the original DLL.
  61. //
  62. //        1.1a - August '92 - added ability to specify menu name; changed
  63. //                            SendMessage(...WM_CLOSE...) to PostMessage(...WM_CLOSE...)
  64. //                            since it seemed to make more sense.
  65. //
  66. //    Notes:
  67. //        Portions of the base code for this utility is based on, and borrowed
  68. //        from, the XTENSION sample code provided with the Microsoft Windows
  69. //        3.1 SDK.
  70. //
  71. //        FM_UTILS attempts to close any auto-started utilities by posting a
  72. //        WM_CLOSE to it's main window. There's no guarantee that this method
  73. //        will work for *all* programs. If you know of a better way to handle
  74. //        this situation, I'd be *really* happy to hear about it.
  75. //
  76. //        While it's not properly documented anywhere, it seems that the maximum
  77. //        command line to pass to WinExec() is 128 characters. Since we have to
  78. //        pass in complete pathnames for all files in order for this to work,
  79. //        we may not be able to pass *all* selected file names.
  80. //
  81. //    Copyright (c) 1992 by Brad P. Smith - all rights reserved
  82. //
  83. //        snail:    R.R. #2 - 777 Crozier Rd.
  84. //                Oxford Mills, Ontario, CANADA
  85. //                K0G 1S0
  86. //        E-Mail:    'B.P.Smith' on BIX, or 'smithb@cognos.com' on Internet
  87. //
  88. //        This utility DLL is FreeWare. Don't let *anybody* charge you
  89. //        for it.
  90. //        However, I would *really* appreciate hearing from you if you find
  91. //        FM_UTILS useful. Drop me a postcard or E-mail. I could use the moral
  92. //        support. :-)
  93. //
  94. //***************************************************************************
  95. #include <windows.h>
  96. #include <string.h>
  97. #include <direct.h>
  98. #include <wfext.h>
  99.  
  100. #include "fm_utils.h"
  101.  
  102. //******************** Global Variables
  103.  
  104. HANDLE    ghDllInst;                        // DLL's instance handle
  105. HMENU    ghMenu;                            // Extension's menu handle
  106. WORD    gwMenuDelta;                    // Delta for extension's menu items
  107.  
  108. USERPROG    UserProg[ NUM_USERPROGS ];
  109. PRELOADPROG    PreLoadProg[ NUM_PRELOADPROGS ];
  110. char        szTempBuffer[ PATH_NAME_LEN ];
  111.  
  112. //***************************************************************************
  113. //
  114. //    LibMain()
  115. //
  116. //    Purpose:
  117. //        LibMain is called by LibEntry. LibEntry is called by Windows
  118. //        when the DLL is loaded. The LibEntry routine is provided
  119. //        in the LIBENTRY.OBJ in the SDK Link Libraries disk. (The
  120. //        source LIBENTRY.ASM is also provided.)
  121. //
  122. //        LibEntry initializes the DLL's heap if a HEAPSIZE value is
  123. //        specified in the DLL's DEF file. After this, LibEntry calls
  124. //        LibMain. The LibMain function below satisfies that call.
  125. //
  126. //        The LibMain function should perform additional initialization
  127. //        tasks required by the DLL. In this DLL, no initialization
  128. //        tasks are required; only the DLL's instance handle is saved. 
  129. //        LibMain should return a value of TRUE if the initialization is 
  130. //        successful.
  131. //
  132. //    Parameters:
  133. //        hLibInst        - DLLs instance handle
  134. //        wDataSeg        - Data segment
  135. //        cbHeapSize        - Size of the DLL's heap
  136. //        lpszCmdLine     - Command line
  137. //
  138. //    Return Value:
  139. //        TRUE if the initialization is successful; FALSE otherwise.
  140. //
  141. //***************************************************************************
  142. int CALLBACK LibMain( HANDLE hLibInst, WORD wDataSeg,
  143.                         WORD cbHeapSize, LPSTR lpszCmdLine )
  144. {
  145.     ghDllInst = hLibInst;
  146.     return TRUE;
  147. }
  148.  
  149. //***************************************************************************
  150. //
  151. //    WEP()
  152. //
  153. //    Purpose:
  154. //        Performs cleanup tasks when the .DLL is unloaded. The WEP() is
  155. //        called automatically by Windows when the DLL is unloaded.
  156. //        
  157. //        Make sure that the WEP() is @1 RESIDENTNAME in the EXPORTS
  158. //        section of the .DEF file. This ensures that the WEP() can
  159. //        be called as quickly as possible. Incidently, this is why
  160. //        the WEP() is called the WEP() instead of WindowsExitProcedure().
  161. //        It takes up the minimum amount of space and is quickly located.
  162. //
  163. //    Parameters:
  164. //        bSystemExit     - Type of exit
  165. //
  166. //    Return Value:
  167. //        TRUE.
  168. //
  169. //***************************************************************************
  170. int CALLBACK WEP( int bSystemExit )
  171. {
  172.     return TRUE;
  173. }
  174.  
  175.  
  176. //***************************************************************************
  177. //
  178. //    FMExtensionProc()
  179. //
  180. //    Purpose:
  181. //        This is an application-defined callback function. It processes menu 
  182. //        commands and messages sent to FM_UTILS.DLL.
  183. //
  184. //    Parameters:
  185. //        hWndExtension    - Identifies the File Manager window
  186. //        wMessage        - Message sent to extension DLL
  187. //        lParam            - Message information
  188. //
  189. //    Return Value:
  190. //        When the wMessage is FMEVENT_LOAD, the handle to extension's menu
  191. //        should be returned; otherwise a NULL value.
  192. //
  193. //***************************************************************************
  194. HMENU CALLBACK FMExtensionProc( HWND hWndExtension, WORD wMessage, LONG lParam )
  195. {
  196.     LPFMS_LOAD    lpload;
  197.     FARPROC    lpDialogProc;
  198.  
  199.     switch( wMessage )
  200.     {
  201.         case FMEVENT_LOAD:
  202.             lpload    = (LPFMS_LOAD) lParam;
  203.  
  204.         // Assign the menu handle from the DLL's resource
  205.  
  206.             ghMenu = LoadMenu( ghDllInst, "UtilsExtensionMenu" );
  207.             lpload->hMenu = ghMenu;
  208.  
  209.         // This is the delta we are being assigned.
  210.  
  211.             gwMenuDelta = lpload->wMenuDelta;
  212.  
  213.             lpload->dwSize = sizeof( FMS_LOAD );
  214.         
  215.         // Assign the popup menu name for this extension
  216.  
  217.             NameMenu( lpload->szMenuName );
  218.  
  219.             InitializeMenu( ghMenu );
  220.             PreLoadPrograms();
  221.             return ghMenu;
  222.  
  223.     // Kill any utilities we started, and free up allocated memory
  224.  
  225.         case FMEVENT_UNLOAD:
  226.             KillPreLoadedPrograms();
  227.             CleanUpMenu();
  228.             break;
  229.  
  230.         case IDM_ABOUT:
  231.             lpDialogProc = (FARPROC)AboutDlgProc;
  232.             DialogBoxParam( ghDllInst, "About", hWndExtension,
  233.                             lpDialogProc, (LONG)hWndExtension );
  234.             break;
  235.  
  236.         default:
  237.  
  238.         // If one of our menu items chosen, process it, otherwise ignore
  239.  
  240.             if( wMessage >= IDM_USERPROG && wMessage <= IDM_MAXUSERPROG )
  241.                 UserProgram( wMessage-IDM_USERPROG, hWndExtension );
  242.             break;
  243.     }
  244.     return NULL;
  245. }
  246.  
  247. //***************************************************************************
  248. //
  249. //    NameMenu()
  250. //
  251. //    Purpose:
  252. //        We parse WINFILE.INI, looking for an entry 'UserMenuName='. If found
  253. //        we use *that* name for our added menu, otherwise we use the default
  254. //        "&Utilities"
  255. //    
  256. //    Parameters:
  257. //        szName - the buffer to put the Menu Name into.
  258. //
  259. //    Return Value:
  260. //        none.
  261. //
  262. //    Notes:
  263. //        We don't perform *any* validity checking on the string, so it is quite
  264. //        possible for the user to specify "&File" (for example) and totally
  265. //        confuse him/herself (but not Windows, fortunately).
  266. //
  267. //***************************************************************************
  268. void NameMenu( LPSTR szName )
  269. {
  270.     GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
  271.             (LPSTR)"UserMenuName", (LPSTR)"&Utilities", szName, MENU_TEXT_LEN,
  272.             (LPSTR)"WINFILE.INI" );
  273. }
  274.  
  275.  
  276. //***************************************************************************
  277. //
  278. //    InitializeMenu()
  279. //
  280. //    Purpose:
  281. //        We parse WINFILE.INI, looking for the list of User Programs and
  282. //        descriptions to add to our menu. To save time, we dynamically
  283. //        allocate buffers to store the program names & descriptions.
  284. //        As we find each complete entry, we add it to the menu.
  285. //    
  286. //    Parameters:
  287. //        none.
  288. //
  289. //    Return Value:
  290. //        none.
  291. //
  292. //    Notes:
  293. //        We really don't *need* to store the Description, because it's of
  294. //        little use to us once it's added to the menu. However, we may
  295. //        someday find use for it, so we may as well keep it.
  296. //
  297. //***************************************************************************
  298. int InitializeMenu( HMENU hMenu )
  299. {
  300.     WORD wProgNum;
  301.     int iNumItems = 0;
  302.     char szUserProgEntry[ ENTRY_TAG_LENGTH ];
  303.  
  304.     for( wProgNum = 0; wProgNum < NUM_USERPROGS; ++wProgNum )
  305.     {
  306.         UserProg[wProgNum].fActive = FALSE;
  307.  
  308.     // Get the Utility's path
  309.  
  310.         wsprintf( szUserProgEntry, "UserProg%u", wProgNum+1 );
  311.         GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
  312.                 (LPSTR)szUserProgEntry, "", szTempBuffer, PATH_NAME_LEN,
  313.                 (LPSTR)"WINFILE.INI" );
  314.         if( !( strlen( szTempBuffer )))
  315.             break;
  316.         else
  317.         {
  318.             UserProg[wProgNum].szPath=(NPSTR)LocalAlloc( LPTR, strlen( szTempBuffer )+1 );
  319.             if( !UserProg[wProgNum].szPath )
  320.             {
  321.                 MessageBox( NULL, "Memory Allocation Error\nCreating Extension Menu",
  322.                     "File Manager", MB_ICONEXCLAMATION | MB_OK );
  323.                 break;
  324.             }
  325.             strcpy( UserProg[wProgNum].szPath, szTempBuffer );
  326.         }
  327.  
  328.     // Get the Description to add to the menu
  329.  
  330.         wsprintf( szUserProgEntry, "UserDesc%u", wProgNum+1 );
  331.         GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
  332.                 (LPSTR)szUserProgEntry, "", szTempBuffer, PROG_DESC_LENGTH,
  333.                 (LPSTR)"WINFILE.INI" );
  334.         if( !( strlen( szTempBuffer )))
  335.         {
  336.             LocalFree( (HLOCAL)UserProg[wProgNum].szPath );
  337.             break;
  338.         }
  339.         else
  340.         {
  341.             UserProg[wProgNum].szDescription=(NPSTR)LocalAlloc( LPTR, strlen( szTempBuffer )+1 );
  342.             if( !UserProg[wProgNum].szDescription )
  343.             {
  344.                 LocalFree( (HLOCAL)UserProg[wProgNum].szPath );
  345.                 MessageBox( NULL, "Memory Allocation Error\nCreating Extension Menu",
  346.                     "File Manager", MB_ICONEXCLAMATION | MB_OK );
  347.                 break;
  348.             }
  349.             strcpy( UserProg[wProgNum].szDescription, szTempBuffer );
  350.         }
  351.  
  352.     // If all is okay, prepare to add this item to the menu
  353.  
  354.         UserProg[wProgNum].fActive = TRUE;
  355.         AppendMenu( hMenu, MF_STRING | MF_ENABLED,
  356.                 IDM_USERPROG+wProgNum, (LPSTR)UserProg[wProgNum].szDescription );
  357.         ++iNumItems;
  358.     }
  359.     return iNumItems;
  360. }
  361.  
  362.  
  363. //***************************************************************************
  364. //
  365. //    CleanUpMenu()
  366. //
  367. //    Purpose:
  368. //        To be nice folks, we go through and free up all of our allocated
  369. //        memory.
  370. //    
  371. //    Parameters:
  372. //        none.
  373. //
  374. //    Return Value:
  375. //        none.
  376. //
  377. //***************************************************************************
  378. void CleanUpMenu()
  379. {
  380.     WORD wProgNum;
  381.  
  382.     for( wProgNum = 0; wProgNum < NUM_USERPROGS; ++wProgNum )
  383.     {
  384.         if( !UserProg[wProgNum].fActive )
  385.             break;
  386.  
  387.         LocalFree( (HLOCAL)UserProg[wProgNum].szPath );
  388.         LocalFree( (HLOCAL)UserProg[wProgNum].szDescription );
  389.         UserProg[wProgNum].fActive = FALSE;
  390.     }
  391. }
  392.  
  393.  
  394. //***************************************************************************
  395. //
  396. //    PreLoadPrograms()
  397. //
  398. //    Purpose:
  399. //        Parse the name of a utility to run, and attempt to start it using
  400. //        WinExec(). If we're successful, we determine and keep the window
  401. //        handle of the main WndProc for that program. We'll need that later to
  402. //        close the program.
  403. //    
  404. //    Parameters:
  405. //        none.
  406. //
  407. //    Return Value:
  408. //        none.
  409. //
  410. //***************************************************************************
  411. void PreLoadPrograms()
  412. {
  413.     WORD wProgNum;
  414.     char szPreLoadProgEntry[ ENTRY_TAG_LENGTH ];
  415.  
  416.     for( wProgNum = 0; wProgNum < NUM_PRELOADPROGS; ++wProgNum )
  417.     {
  418.         PreLoadProg[wProgNum].fActive = FALSE;
  419.  
  420.     // Get the Utility's path
  421.  
  422.         wsprintf( szPreLoadProgEntry, "PreLoad%u", wProgNum+1 );
  423.         GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
  424.                 (LPSTR)szPreLoadProgEntry, "", szTempBuffer, PATH_NAME_LEN,
  425.                 (LPSTR)"WINFILE.INI" );
  426.         if( !( strlen( szTempBuffer )))
  427.             break;
  428.         else
  429.         {
  430.             PreLoadProg[wProgNum].szPath=(NPSTR)LocalAlloc( LPTR, strlen( szTempBuffer )+1 );
  431.             if( !PreLoadProg[wProgNum].szPath )
  432.             {
  433.                 MessageBox( NULL, "Memory Allocation Error\nPre-Loading Programs",
  434.                     "File Manager", MB_ICONEXCLAMATION | MB_OK );
  435.                 break;
  436.             }
  437.             strcpy( PreLoadProg[wProgNum].szPath, szTempBuffer );
  438.         }
  439.  
  440.     // If all is okay, attempt start the program
  441.  
  442.         PreLoadProg[wProgNum].hInst =
  443.                 WinExec( (LPSTR)PreLoadProg[wProgNum].szPath, SW_SHOWMINNOACTIVE );
  444.  
  445.     // If we failed, don't bother to notify. Otherwise, determine the handle
  446.  
  447.         if( PreLoadProg[wProgNum].hInst >= 32 )
  448.         {
  449.             PreLoadProg[wProgNum].fActive = TRUE;
  450.             PreLoadProg[wProgNum].hWnd =
  451.                     DetermineProgramHandle( PreLoadProg[wProgNum].hInst );
  452.         }
  453.     }
  454. }
  455.  
  456.  
  457. //***************************************************************************
  458. //
  459. //    DetermineProgramHandle()
  460. //
  461. //    Purpose:
  462. //        Given the Instance Handle of a program, we want to find out the Window
  463. //        handle of its main WndProc.
  464. //    
  465. //    Parameters:
  466. //        hInst - Instance handle of the app we're trying to find the handle of.
  467. //
  468. //    Return Value:
  469. //        hWnd - Window handle of app's main WndProc
  470. //
  471. //***************************************************************************
  472. HWND DetermineProgramHandle( HINSTANCE hInst )
  473. {
  474.     HWND hWnd;
  475.     HWND hWndParent;
  476.  
  477. // Find the first top-level window that matches our selected instance handle.
  478.  
  479.     for( hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
  480.         hWnd;
  481.         hWnd = GetWindow( hWnd, GW_HWNDNEXT ) )
  482.     {
  483.         if( GetWindowWord( hWnd, GWW_HINSTANCE ) == hInst )
  484.             break;
  485.     }
  486.  
  487. // Iconized programs can have *two* top-level window handles, the icon
  488. // (or main window), and the icon title.
  489. // In case we didn't get the top-most window of this task, loop through
  490. // until GetParent returns NULL. Then, we have the window we want to send
  491. // our WM_CLOSE to.
  492.  
  493.     while( hWndParent = GetParent( hWnd ) )
  494.         hWnd = hWndParent;
  495.  
  496.     return hWnd;
  497. }
  498.  
  499.  
  500. //***************************************************************************
  501. //
  502. //    KillPreLoadedPrograms()
  503. //
  504. //    Purpose:
  505. //        We step through the window handles of the preloaded apps, and send
  506. //        WM_CLOSE messages to them. Naturally, we check first to ensure that
  507. //        the window handle is still valid.
  508. //    
  509. //    Parameters:
  510. //        none.
  511. //
  512. //    Return Value:
  513. //        none.
  514. //
  515. //    Notes:
  516. //        We have no guarantee that, when the main WndProc of the application
  517. //        receives a WM_CLOSE message, that it will actually terminate the
  518. //        app. If somebody knows of a better way to handle this, I'd be *real*
  519. //        happy to hear about it.
  520. //
  521. //***************************************************************************
  522. void KillPreLoadedPrograms()
  523. {
  524.     WORD wProgNum;
  525.  
  526.     for( wProgNum = 0; wProgNum < NUM_PRELOADPROGS; ++wProgNum )
  527.     {
  528.         if( !PreLoadProg[wProgNum].fActive )
  529.             break;
  530.  
  531.     // Check to make sure the Window is still active, and still matches
  532.     // the instance handle. If so, attempt to close it.
  533.  
  534.         if(( IsWindow( PreLoadProg[wProgNum].hWnd )) &&
  535.             ( GetWindowWord( PreLoadProg[wProgNum].hWnd, GWW_HINSTANCE ) ==
  536.             PreLoadProg[wProgNum].hInst ))
  537.         {
  538.             PostMessage( PreLoadProg[wProgNum].hWnd, WM_CLOSE, 0, 0L );
  539.         }
  540.         LocalFree( (HLOCAL)PreLoadProg[wProgNum].szPath );
  541.         PreLoadProg[wProgNum].fActive = FALSE;
  542.     }
  543. }
  544.  
  545.  
  546. //***************************************************************************
  547. //
  548. //    UserProgram()
  549. //
  550. //    Purpose:
  551. //        This is where we select which User Program has been selected to
  552. //        run. If there are one or more files selected, we generate a command
  553. //        line, and then execute it using WinExec().
  554. //    
  555. //    Parameters:
  556. //        wProgNum        - Identifies the index of the user program to run
  557. //        hWndExtension    - Identifies the File Manager window
  558. //
  559. //    Return Value:
  560. //        none.
  561. //
  562. //    Notes:
  563. //        While it doesn't seem to be specifically documented anywhere, the
  564. //        maximum number of characters in a command line can't exceed 128.
  565. //        Since it's possible to select any number of files, it's pretty easy
  566. //        to blow this limit. In order to maximize our use of this limited
  567. //        space, we perform some trickery.
  568. //        Since we know that File Manager (currently) doesn't allow selection
  569. //        of multiple files across different directories or drives, we know
  570. //        that the path portion of all selected files should be the same. So,
  571. //        we determine the path portion, and actually make that directory our
  572. //        current directory, and chop the path portion from all of the selected
  573. //        files (WinExec() will search for the utility, if its full path is
  574. //        not specified, using the standard Windows search mechanism).
  575. //        If we still try to blow the length limit, a warning MessageBox will
  576. //        pop up, and the command line will be truncated at the last possble
  577. //        file addition. While actually *changing* the file selections in
  578. //        File Manager, it looks like this isn't feasible.
  579. //
  580. //***************************************************************************
  581. void UserProgram( WORD wProgNum, HWND hWndExtension )
  582. {
  583.     static FMS_GETFILESEL fmsFileInfo;
  584.     NPSTR szCmdLine;
  585.     WORD wSelFileCount;
  586.     WORD wProgIndex;
  587.     WORD wWinExecReturn;
  588.     WORD wCmdLineLength;
  589.     char *szFileName;
  590.  
  591.     if( !UserProg[wProgNum].fActive )
  592.         return;
  593.  
  594.     wSelFileCount = (WORD)SendMessage( hWndExtension, FM_GETSELCOUNTLFN, 0, 0L );
  595. //    if( !wSelFileCount )
  596. //    {
  597. //        MessageBox( NULL, "No Files Selected!", "File Manager",
  598. //                MB_ICONEXCLAMATION | MB_OK );
  599. //        return;
  600. //    }
  601.  
  602.     szCmdLine = (NPSTR)LocalAlloc( LPTR, strlen( UserProg[wProgNum].szPath )+1 );
  603.     if( !szCmdLine )
  604.     {
  605.         MessageBox( NULL, "Unable to create command line!", "File Manager",
  606.                 MB_ICONEXCLAMATION | MB_OK );
  607.         return;
  608.     }
  609.     strcpy( szCmdLine, UserProg[wProgNum].szPath );
  610.  
  611.     for( wProgIndex = 0; wProgIndex < wSelFileCount; wProgIndex++ )
  612.     {
  613.         SendMessage( hWndExtension, FM_GETFILESELLFN, wProgIndex,
  614.                     (LONG)(LPFMS_GETFILESEL)&fmsFileInfo );
  615.  
  616.     //    We know that all of the selected files will be in the same directory,
  617.     //    because File Manager doesn't allow selection across drives or
  618.     //    directories. We can use this to our advantage by determining the
  619.     //    selected directory, making it the current directory, and only putting
  620.     //    the actual file names on the command line. This maximizes our use of
  621.     //    the command line (which, remember, is limited to 128 characters).
  622.  
  623.         if( wProgIndex == 0 )
  624.         {
  625.             strcpy( szTempBuffer, fmsFileInfo.szName );
  626.             szFileName = strrchr( szTempBuffer, '\\' );
  627.  
  628.         // We don't chop the trailing '\' on root directory
  629.  
  630.             if( (WORD)( szFileName - szTempBuffer ) > 3 )
  631.                 szFileName[0] = 0;
  632.             else
  633.                 szFileName[1] = 0;
  634.  
  635.             _chdrive( szTempBuffer[0] - 'A' + 1 );
  636.             _chdir( szTempBuffer );
  637. //            OutputDebugString( (LPSTR)szTempBuffer );
  638. //            OutputDebugString( (LPSTR)"\n" );
  639.         }
  640.  
  641.         szFileName = (char *)( strrchr( fmsFileInfo.szName, '\\' ) + 1 );
  642.  
  643.         wCmdLineLength = strlen( szCmdLine ) + strlen( szFileName ) + 2;
  644.         if( wCmdLineLength <= MAX_CMDLINE_LENGTH )
  645.         {
  646.             szCmdLine = (NPSTR)LocalReAlloc( (HLOCAL)szCmdLine,
  647.                     wCmdLineLength, LMEM_ZEROINIT );
  648.             if( !szCmdLine )
  649.             {
  650.                 LocalFree( (HLOCAL)szCmdLine );
  651.                 MessageBox( NULL, "Unable to construct command line!", "File Manager",
  652.                         MB_ICONEXCLAMATION | MB_OK );
  653.                 return;
  654.             }
  655.  
  656.             strcat( szCmdLine, " " );
  657.             strcat( szCmdLine, szFileName );
  658.         }
  659.         else
  660.         {
  661.             wsprintf( szTempBuffer,
  662. "Adding file path\n%s\nwould make command line too long.\nTruncating command line.",
  663.                     (LPSTR)fmsFileInfo.szName );
  664.             MessageBox( NULL, szTempBuffer, "File Manager", MB_ICONEXCLAMATION | MB_OK );
  665.             break;
  666.         }
  667.     }
  668.  
  669. //    OutputDebugString( (LPSTR)szCmdLine );
  670. //    OutputDebugString( (LPSTR)"\n" );
  671.  
  672.     wWinExecReturn = WinExec( szCmdLine, SW_SHOWNORMAL );
  673.     LocalFree( (HLOCAL)szCmdLine );
  674.     if( wWinExecReturn < 32 )
  675.     {
  676.         wsprintf( szTempBuffer, "Unable to Execute\n%s", UserProg[wProgNum].szPath );
  677.         MessageBox( NULL, szTempBuffer, "File Manager", MB_ICONEXCLAMATION | MB_OK );
  678.     }
  679. }
  680.  
  681.  
  682. //***************************************************************************
  683. //
  684. //    AboutDlgProc()
  685. //
  686. //    Purpose:
  687. //        Your typical "About" box, in case you forget who wrote this amazing
  688. //        piece of code.    :-)
  689. //    
  690. //    Parameters:
  691. //        The usual DLGPROC parameters
  692. //
  693. //    Return Value:
  694. //        The typical DLGPROC BOOL return
  695. //
  696. //***************************************************************************
  697. BOOL CALLBACK AboutDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam )
  698. {
  699.     switch( wMsg )
  700.     {
  701.         case WM_COMMAND:
  702.             switch( wParam )
  703.             {
  704.                 case IDOK:
  705.                 case IDCANCEL:
  706.                     EndDialog( hDlg, TRUE );
  707.                     break;
  708.  
  709.                 default:
  710.                     return FALSE;
  711.             }
  712.             return TRUE;
  713.  
  714.         default:
  715.             break;
  716.     }
  717.     return FALSE;
  718. }
  719.  
  720.